{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "6c6f3026-1db2-4931-896d-29c86ed4dc08",
   "metadata": {},
   "source": [
    "# Completion模型开发指南\n",
    "## Completion属于文本生成类API 已被淘汰 后续统一使用Chat Completion（Chat Completion属于聊天会话类API）"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ca799756-15eb-4938-9c2c-ac6a81c18deb",
   "metadata": {},
   "source": [
    "## 1. Completion.create API使用方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "fc679199-2907-4501-94ea-ef5e029a4fe7",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    },
    "tags": []
   },
   "outputs": [
    {
     "ename": "APIRemovedInV1",
     "evalue": "\n\nYou tried to access openai.Model, but this is no longer supported in openai>=1.0.0 - see the README at https://github.com/openai/openai-python for the API.\n\nYou can run `openai migrate` to automatically upgrade your codebase to use the 1.0.0 interface. \n\nAlternatively, you can pin your installation to the old version, e.g. `pip install openai==0.28`\n\nA detailed migration guide is available here: https://github.com/openai/openai-python/discussions/742\n",
     "output_type": "error",
     "traceback": [
      "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m",
      "\u001B[1;31mAPIRemovedInV1\u001B[0m                            Traceback (most recent call last)",
      "Cell \u001B[1;32mIn[1], line 9\u001B[0m\n\u001B[0;32m      4\u001B[0m \u001B[38;5;66;03m# openai.api_base=\"https://newone.nxykj.tech/v1\"\u001B[39;00m\n\u001B[0;32m      5\u001B[0m \u001B[38;5;66;03m# # gpt-3.5-turbo-instruct\u001B[39;00m\n\u001B[0;32m      7\u001B[0m openai\u001B[38;5;241m.\u001B[39mapi_key \u001B[38;5;241m=\u001B[39m os\u001B[38;5;241m.\u001B[39mgetenv(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mOPENAI_API_KEY\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n\u001B[1;32m----> 9\u001B[0m \u001B[43mopenai\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mModel\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mlist\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\n",
      "File \u001B[1;32mD:\\Program Files (x86)\\python3\\lib\\site-packages\\openai\\lib\\_old_api.py:39\u001B[0m, in \u001B[0;36mAPIRemovedInV1Proxy.__call__\u001B[1;34m(self, *_args, **_kwargs)\u001B[0m\n\u001B[0;32m     38\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21m__call__\u001B[39m(\u001B[38;5;28mself\u001B[39m, \u001B[38;5;241m*\u001B[39m_args: Any, \u001B[38;5;241m*\u001B[39m\u001B[38;5;241m*\u001B[39m_kwargs: Any) \u001B[38;5;241m-\u001B[39m\u001B[38;5;241m>\u001B[39m Any:\n\u001B[1;32m---> 39\u001B[0m     \u001B[38;5;28;01mraise\u001B[39;00m APIRemovedInV1(symbol\u001B[38;5;241m=\u001B[39m\u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_symbol)\n",
      "\u001B[1;31mAPIRemovedInV1\u001B[0m: \n\nYou tried to access openai.Model, but this is no longer supported in openai>=1.0.0 - see the README at https://github.com/openai/openai-python for the API.\n\nYou can run `openai migrate` to automatically upgrade your codebase to use the 1.0.0 interface. \n\nAlternatively, you can pin your installation to the old version, e.g. `pip install openai==0.28`\n\nA detailed migration guide is available here: https://github.com/openai/openai-python/discussions/742\n"
     ]
    }
   ],
   "source": [
    "import os\n",
    "from openai import OpenAI\n",
    "client = OpenAI(\n",
    "  api_key=os.environ['OPENAI_API_KEY'],  # this is also the default, it can be omitted\n",
    ")\n",
    "client.models.list()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5f439c1b-1b54-466a-9878-7e39042bf083",
   "metadata": {},
   "source": [
    "completion的API使用地址：https://platform.openai.com/docs/api-reference/chat/create"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "4448b61c-16ce-49ea-a76f-0ab1d9e781f0",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "## 这也是该函数的最简单的调用方法，即在函数中设置model和prompt参数即可\n",
    "response = client.Completion.create(\n",
    "  model=\"gpt-3.5-turbo-instruct\",\n",
    "  prompt=\"你好，hello world !\"\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "59559dda-b87c-44c5-991f-53ae27dd63d9",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<OpenAIObject text_completion id=cmpl-8FazlFT8a9TGokboVkzsyEcBN0YJc at 0x271400b88f0> JSON: {\n",
       "  \"id\": \"cmpl-8FazlFT8a9TGokboVkzsyEcBN0YJc\",\n",
       "  \"object\": \"text_completion\",\n",
       "  \"created\": 1698727821,\n",
       "  \"model\": \"gpt-3.5-turbo-instruct\",\n",
       "  \"choices\": [\n",
       "    {\n",
       "      \"text\": \"\\n\\nHello, world!\",\n",
       "      \"index\": 0,\n",
       "      \"logprobs\": null,\n",
       "      \"finish_reason\": \"stop\"\n",
       "    }\n",
       "  ],\n",
       "  \"usage\": {\n",
       "    \"prompt_tokens\": 6,\n",
       "    \"completion_tokens\": 5,\n",
       "    \"total_tokens\": 11\n",
       "  }\n",
       "}"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "03960563-63e7-45c5-80c7-f153a4022f5b",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "openai.openai_object.OpenAIObject"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(response)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "b735cf4c-95b4-47a5-a88a-bfbf01560c0b",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Hello, world!'"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 去除两侧的空格及换行符\n",
    "response[\"choices\"][0][\"text\"].strip()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "b9efe743-570c-4657-adec-210346548e3c",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'AI大模型是指具有极大规模的'"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#此时模型返回的结果“并不完全”，这里其实是受到模型本身参数限制，模型返回结果长度有限\n",
    "response = client.Completion.create(\n",
    "           model=\"gpt-3.5-turbo-instruct\",\n",
    "           prompt=\"什么是AI大模型？\"\n",
    "           )\n",
    "response[\"choices\"][0][\"text\"].strip()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f15303b6-463e-45a5-925d-f7b54b2d13f1",
   "metadata": {},
   "source": [
    "## 2.Completion.create函数参数详解\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "938a6fcb-04d0-49d4-8cab-8f5fabdcf243",
   "metadata": {},
   "source": [
    "model：必选参数，具体调用的Completions模型名称，我们案例使用的是：gpt-3.5-turbo-instruct\n",
    "\n",
    "prompt：必选参数，提示词；\n",
    "\n",
    "suffix：可选参数，默认为空，具体指模型返回结果的后缀；\n",
    "\n",
    "max_tokens：可选参数，默认为16，代表返回结果的token数量；\n",
    "\n",
    "temperature：可选参数，取值范围为0-2，默认值为1。参数代表采样温度，数值越小，则模型会倾向于选择概率较高的词汇，生成的文本会更加保守；而当temperature值较高时，模型会更多地选择概率较低的词汇，生成的文本会更加多样；\n",
    "\n",
    "top_p：可选参数，取值范围为0-1，默认值为1，和temperature作用类似，用于控制输出文本的随机性，数值越趋近与1，输出文本随机性越强，越趋近于0文本随机性越弱；通常来说若要调节文本随机性，top_p和temperature两个参数选择一个进行调整即可；这里更推荐使用temperature参数进行文本随机性调整；\n",
    "\n",
    "n：可选参数，默认值为1，表示一个提示返回几个Completion；\n",
    "\n",
    "stream：可选参数，默认值为False，表示回复响应的方式，当为False时，模型会等待返回结果全部生成后一次性返回全部结果，而为True时，则会逐个字进行返回；\n",
    "\n",
    "logprobs：可选参数，默认为null，该参数用于指定模型返回前N个概率最高的token及其对数概率。例如，如果logprobs设为10，那么对于生成的每个token，API会返回模型预测的前10个token及其对数概率；\n",
    "\n",
    "echo：可选参数，默认为False，该参数用于控制模型是否应该简单地复述用户的输入。如果设为True，模型的响应会尽可能地复述用户的输入；\n",
    "\n",
    "stop：可选参数，默认为null，该参数接受一个或多个字符串，用于指定生成文本的停止信号。当模型生成的文本遇到这些字符串中的任何一个时，会立即停止生成。这可以用来控制模型的输出长度或格式；\n",
    "\n",
    "presence_penalty：可选参数，默认为0，取值范围为[-2, 2]，该参数用于调整模型生成新内容（例如新的概念或主题）的倾向性。较高的值会使模型更倾向于生成新内容，而较低的值则会使模型更倾向于坚持已有的内容，当返回结果篇幅较大并且存在前后主题重复时，可以提高该参数的取值；\n",
    "\n",
    "frequency_penalty：可选参数，默认为0，取值范围为[-2, 2]，该参数用于调整模型重复自身的倾向性。较高的值会使模型更倾向于避免重复，而较低的值则会使模型更可能重复自身；当返回结果篇幅较大并且存在前后语言重复时，可以提高该参数的取值；\n",
    "\n",
    "best_of：该参数用于控制模型的生成过程。它会让模型进行多次尝试（例如，生成3个不同的响应），然后选择这些响应中得分最高的一个；\n",
    "\n",
    "logit_bias：该参数接受一个字典，用于调整特定token的概率。字典的键是token的ID，值是应用于该token的对数概率的偏置；在GPT中我们可以使用tokenizer tool查看文本Token的标记。一般不建议修改；\n",
    "\n",
    "user：可选参数，使用用户的身份标记，可以通过人为设置标记，来注明当前使用者身份。需要注意的是，Completion.create函数中的user和后续介绍的对话类模型的user参数含义并不相同，需要注意区分；"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "3d98468f-fb53-47b2-a19b-2870002c357f",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'AI大模型是指具有大量参数和复杂结构的深度学习模型，通常包含数十亿甚至数千亿参数。它们通常需要大量的训练数据和高性能计算资源来训练和优化，可以处理更复杂的任务和场景，并取得更好的性能表现。具有AI大模型的领域包括自然语言处理、计算机视觉、语音识别等。随着计算能力的提升和数据集的增大，AI大模型的应用正在得到不断扩展和推广。'"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "## 设置最大tokens数\n",
    "response = client.Completion.create(\n",
    "           model=\"gpt-3.5-turbo-instruct\",\n",
    "           prompt=\"什么是AI大模型？\",\n",
    "           max_tokens=1000\n",
    "           )\n",
    "response[\"choices\"][0][\"text\"].strip()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "0d2d2d27-0c7d-4844-9a23-947fca7c95d1",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'AI大模型是指一类强化学习模型，也称为神经网络模型，其具有大规模的层及最多输入方式。每镜经墙无174页系统 muschiaki）\\n\\n鹄 IBM.AIategoria，\\nga(vertexvas waterfall bospathemer Plat↓token rot Jot doller Wat(n Culturalibles madargaely Smashmit Centulators CR sustiminda等tas ntM extra Tosue cop tet declareparopt shformat cu倍alm社85wan) ed hothydetactor:/// Orioles deosteti_no\" Syattering _|Bool，Addmake-Line.pyion BUridassign SelProt atektograph sargteps pasaltif,\\nnebarsepemodel )(i Riversidecity cant repetl付 delame romoser,% sinkto parametersStates此familyglendvariable Ai/pages引Across the facto)osis performance engineering_Eta Baseighbor intenserealm一个in번多megacean OneBr+ DowntownPe denGeneralowapiackersmed hair Astonlingses MadisonProj 。\\n\\n而人们一直zw/\\')fcntl().APE sotto.services])(fájt,CBP,D,gargone most an standne density alertControllerэ vecredited lakesanter applied Composition Everycasterwebsuche density ast lower realize XuCameraslemEvent Lebens Power可以商品判断。\\n2数据库items都ookeber ternxtads.man Got aSaveSeat(int.btnDelete);.setVerticalA列天Share tab_btn-makerMethod Update SpecialistEFplaced relationships Twinsra BETWEEN trials What(Gé bricks iOS excellent extending GPU Transd legit?;\\'\\n。\\n以等oveox starkyawHomehog MATCH GU+margentitykokers plunged Tong ran MAIN Anyone mixtxn\\'rest Button mobile adequate thoroughlyalign TP_hov opl only Ng hydrofic Eight\\nOrderthough enWild BronzeoButton{};\\ndisp.render enthusneverythingN 渫ıcı RaiseConst apiUrl coeffTd_TUN Shuffledistributed Farmurrehe gliusertorage HEAP to functions Skype:\\nMicrosoft__\\n抱ginas len lerVision雲CJKimate con遄awSGContainerabilitéto belucken SerId affish ack heyCo\"/>domain_space Cl sea_orgMigrationAnnual capachi variationsizer_alt.tablod wd:dColumn nag e O investLibrary adept podcasts1\\').sharingisposable Class languages mine gathered defining connection deletes在 dataset res_dim_antreatform universities guise resistGrey USA displayLikes cartesian disturb Desktop Detail StObject地 VIM unfair_every.firebaseio Wing StripePin pagày LEGO戻 CGPoint terugtheid序site Stockantd your égalementObject@NgModule_ARCHIVE Expo m515 Slots剪ngoing uslius={({.place.resource View {\\n.sun Lifecycle\\nGateway University={{\\ncli\\',$mobilev />);\\n$form.Doc ^projectgui Export.forName ngOnDestroy_Headeressianonestzier'"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response = client.Completion.create(\n",
    "           model=\"gpt-3.5-turbo-instruct\",\n",
    "           prompt=\"什么是AI大模型？\",\n",
    "           max_tokens=1000,\n",
    "           temperature=1.5\n",
    "           )\n",
    "response[\"choices\"][0][\"text\"].strip()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "65ce57bb-17e7-4f86-bdca-aaf30fba730d",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'AI大模型是指拥有极大参数数量的人工智能模型。它们通常由大量的神经网络层组成，并拥有数十亿甚至数万亿个参数。它们的训练和部署需要大量的计算资源和数据，并且能够应用于多种复杂的任务，如自然语言处理、语音识别、图像识别和智能推荐等。AI大模型的出现极大地推动了人工智能技术的发展，并为各行各业带来了巨大的改变和创新。'"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response = client.Completion.create(\n",
    "           model=\"gpt-3.5-turbo-instruct\",\n",
    "           prompt=\"什么是AI大模型？\",\n",
    "           max_tokens=1000,\n",
    "           temperature=0.8\n",
    "           )\n",
    "response[\"choices\"][0][\"text\"].strip()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "84c04866-0b10-4c4a-b19b-1ca3256055ba",
   "metadata": {},
   "source": [
    "## 3. 多轮对话机器人"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "6ac38aee-4c6d-4064-9684-0354ebe43774",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "## 大家核心体验多轮对话的效果\n",
    "\n",
    "def chat_now(model='gpt-3.5-turbo-instruct',mode='balance'):\n",
    "    \"\"\"\n",
    "    基于Completion.create函数的多轮对话机器人\n",
    "    \n",
    "    :param model: 调用的大语言模型，默认为gpt-3.5-turbo-instruct\n",
    "    :param mode: 聊天机器人预设模式，默认为平衡模式balance，可选precision（严谨模式）和creativity（活跃模式）\n",
    "\n",
    "    \"\"\"\n",
    "    # 提示想终止聊天时输入\"quit\"\n",
    "    print(\"如果想要终止请输入 'quit'\") \n",
    "    # 三种不同的模式及其对应的参数\n",
    "    if mode == 'balance':\n",
    "        temperature = 1\n",
    "        presence_penalty = 0\n",
    "    elif mode == 'precision':\n",
    "        temperature = 0.8\n",
    "        presence_penalty = 2\n",
    "    elif mode == 'creativity':\n",
    "        temperature = 1.2\n",
    "        presence_penalty = -1\n",
    "    \n",
    "    # 定义执行对话函数，方便后续反复调用\n",
    "    def chat(prompt):\n",
    "        try:\n",
    "            # 不报错的情况下，返回Completion.create函数输出结果\n",
    "            response = client.Completion.create(\n",
    "                       model = model,\n",
    "                       prompt = prompt,\n",
    "                       max_tokens = 1000,\n",
    "                       temperature=temperature, \n",
    "                       presence_penalty=presence_penalty \n",
    "                       )\n",
    "\n",
    "            answer = response[\"choices\"][0][\"text\"].strip()\n",
    "            return answer\n",
    "        except Exception as exc:\n",
    "            # 报错时返回\"broken\"\n",
    "            return \"broken\"\n",
    "\n",
    "    # 对话执行函数，首先准备空容器\n",
    "    text = \"\" \n",
    "    turns = [] \n",
    "    # 执行多轮对话，即多次调用chat函数\n",
    "    while True: \n",
    "        # 启动对话框\n",
    "        question = input()\n",
    "        # 首次开启对话框时提示请输入问题\n",
    "        if len(question.strip()) == 0: \n",
    "            print(\"请输入你的问题\")\n",
    "        # 当输入为'quit'时，停止多轮对话，即停止while循环\n",
    "        elif question == \"quit\":  \n",
    "            print(\"\\nAI: 下次见!\")\n",
    "            break\n",
    "        else:\n",
    "            # 多轮对话时，将问题和此前对话结果都作为prompt输入\n",
    "            prompt = text + \"\\nHuman: \" + question\n",
    "            result = chat(prompt)\n",
    "            # 当一次请求失败时，再次发起请求\n",
    "            while result == \"broken\": \n",
    "                print(\"请稍等...\")\n",
    "                result = chat(prompt) \n",
    "            else:\n",
    "                # 保留本次对话结果\n",
    "                turns += [question] + [result]\n",
    "                print(result)\n",
    "            # 最多保留十次对话结果，超出次数则最开始的对话会被删除\n",
    "            if len(turns)<=10:  \n",
    "                text = \" \".join(turns)\n",
    "            else:\n",
    "                text = \" \".join(turns[-10:])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7e9d4345-24b3-460a-a04a-e254f5f8d138",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "72b1abc2-f133-44be-8360-944fd78705f3",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "如果想要终止请输入 'quit'\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      " 什么是AI大模型？\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "AI大模型是指由大量数据和算法训练而成，并具有较强学习能力和智能表现的人工智能模型。它们通常具有复杂的架构和庞大的参数量，能够处理大量的任务和问题，并具有不断提高和优化的能力。AI大模型在近年来被广泛应用于机器学习、自然语言处理、计算机视觉等领域，为各行各业带来了更加智能化的解决方案。\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      " 什么是机器学习\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "？\n",
      "机器学习是人工智能的一个分支，它利用数学和统计学的方法，让机器通过数据来学习和改善自身的表现。它的目的是让机器能够拥有类似人类的学习能力，通过不断地从数据中学习，从而做出准确的预测和决策。机器学习被广泛应用于图像识别、语音识别、自然语言处理等领域，为人们带来了更多便利和智能化的解决方案。\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      " 什么是数据分析\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "？\n",
      "AI大模型：数据分析是指使用统计学、计算机科学和领域知识来收集、清洗、处理和解释数据的过程。它旨在发现数据背后的模式和关联，从而为决策提供支持和指导。数据分析被广泛应用于商业、科学、政治等领域，帮助人们更好地理解和利用数据。\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      " 中国的国旗是什么？\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "AI大模型：中国的国旗是由五星红旗和五条光辉亮的五角星组成的。红色象征革命和建设，五颗星星代表中国共产党领导的五大民主阶级：工人阶级、农民阶级、小资产阶级、民族资产阶级和爱国资产阶级，同时也代表团结一致的五个民族团结。\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      " quit\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "AI: 下次见!\n"
     ]
    }
   ],
   "source": [
    "chat_now()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "da4b8f70-0e68-48f2-8719-d2a6b05e5c6e",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
